package info.javatech.jmf.components.command.server;

import com.sun.faces.application.MethodBindingMethodExpressionAdapter;
import com.sun.faces.application.MethodExpressionMethodBindingAdapter;
import info.javatech.jmf.common.components.CommandConstants;
import info.javatech.jmf.server.PCommandOwner;
import info.javatech.jmf.server.PComponentHolder;
import info.javatech.jmf.server.protocol.PCommand;
import info.javatech.jmf.server.protocol.PComponent;
import info.javatech.jmf.server.util.EncodeUtils;
import java.io.IOException;
import javax.faces.FacesException;
import javax.faces.component.ActionSource2;
import javax.faces.component.UIComponent;
import javax.faces.component.UIComponentBase;
import javax.faces.context.FacesContext;
import javax.faces.el.MethodBinding;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;
import javax.el.MethodExpression;
import javax.el.ELException;
import javax.el.ValueExpression;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;

public class CommandComponent extends UIComponentBase implements PComponentHolder,ActionSource2{
    
    private boolean immediate = false;
    private boolean immediateSet = false;
    
    /**
     * <p>The {@link MethodExpression} that, when invoked, yields the
     * literal outcome value.</p>
     */
    private MethodExpression actionExpression = null;
    
    private MethodBinding methodBindingActionListener;
    
    
    /** Creates a new instance of CommandComponent */
    public CommandComponent() {
        setRendererType(null);
    }
    
    public String getFamily() {
        return "jmf.command";
    }
    
    public void encodeBegin(FacesContext context) throws IOException {
        
        PCommand command = (PCommand) getPComponent();
        
        UIComponent parent = this.getParent();
        
        if (parent instanceof PCommandOwner) {
            ((PCommandOwner)parent).addCommand(command);
        } else throw new FacesException("Parent of this component is not instance of PCommandOwner");
    }
    
    public void encodeEnd(FacesContext context) throws IOException {
    }
    
    public PComponent getPComponent() {
        PCommand container = new PCommand();
        container.setId(getId());
        container.setType(CommandConstants.TYPE_NAME);
        container.setPropertyNames(new String[]{
            CommandConstants.SHORT_TITLE_PROPERTY_NAME,
            CommandConstants.LONG_TITLE_PROPERTY_NAME,
            CommandConstants.PRIORITY_PROPERTY_NAME,
            CommandConstants.TYPE_PROPERTY_NAME});
        container.setPropertyValues(new String[]{
            (String)ObjectUtils.defaultIfNull(this.getAttributes().get(CommandConstants.SHORT_TITLE_PROPERTY_NAME),""),
            (String)ObjectUtils.defaultIfNull(this.getAttributes().get(CommandConstants.LONG_TITLE_PROPERTY_NAME),""),
            (String)ObjectUtils.defaultIfNull(this.getAttributes().get(CommandConstants.PRIORITY_PROPERTY_NAME),"0"),
            (String)ObjectUtils.defaultIfNull(this.getAttributes().get(CommandConstants.TYPE_PROPERTY_NAME),"1")});
        
        return container;
    }
    
    
    public MethodExpression getActionExpression() {
        return actionExpression;
    }
    
    public void setActionExpression(MethodExpression actionExpression) {
        
        this.actionExpression = actionExpression;
    }
    
    /**
     * @throws NullPointerException {@inheritDoc}
     */
    public void addActionListener(ActionListener listener) {
        
        addFacesListener(listener);
        
    }
    
    public ActionListener[] getActionListeners() {
        
        ActionListener al[] = (ActionListener [])
        getFacesListeners(ActionListener.class);
        return (al);
        
    }
    
    
    
    /**
     * @throws NullPointerException {@inheritDoc}
     */
    public void removeActionListener(ActionListener listener) {
        
        removeFacesListener(listener);
        
    }
    
    public boolean isImmediate() {
        
        if (this.immediateSet) {
            return (this.immediate);
        }
        ValueExpression ve = getValueExpression("immediate");
        if (ve != null) {
            try {
                return (Boolean.TRUE.equals(ve.getValue(getFacesContext().getELContext())));
            } catch (ELException e) {
                throw new FacesException(e);
            }
        } else {
            return (this.immediate);
        }
        
    }
    
    
    public void setImmediate(boolean immediate) {
        
        // if the immediate value is changing.
        if (immediate != this.immediate) {
            this.immediate = immediate;
        }
        this.immediateSet = true;
        
    }
    
    /**
     * {@inheritDoc}
     * @deprecated This has been replaced by {@link #getActionExpression}.
     */
    public MethodBinding getAction() {
        MethodBinding result = null;
        MethodExpression me = null;
        
        if (null != (me = getActionExpression())) {
            // if the MethodExpression is an instance of our private
            // wrapper class.
            if (me.getClass() == MethodExpressionMethodBindingAdapter.class) {
                result = ((MethodExpressionMethodBindingAdapter)me).getWrapped();
            } else {
                // otherwise, this is a real MethodExpression.  Wrap it
                // in a MethodBinding.
                result = new MethodBindingMethodExpressionAdapter(me);
            }
        }
        return result;
        
    }
    
    /**
     * {@inheritDoc}
     * @deprecated This has been replaced by {@link #setActionExpression(javax.el.MethodExpression)}.
     */
    public void setAction(MethodBinding action) {
        MethodExpressionMethodBindingAdapter adapter = null;
        if (null != action) {
            adapter = new MethodExpressionMethodBindingAdapter(action);
            setActionExpression(adapter);
        } else {
            setActionExpression(null);
        }
    }
    
    /**
     * {@inheritDoc}
     * @deprecated Use {@link #getActionListeners} instead.
     */
    public MethodBinding getActionListener() {
        return this.methodBindingActionListener;
    }
    
    /**
     * {@inheritDoc}
     * @deprecated This has been replaced by {@link #addActionListener(javax.faces.event.ActionListener)}.
     */
    public void setActionListener(MethodBinding actionListener) {
        this.methodBindingActionListener = actionListener;
    }
    
    public void decode(FacesContext context) {
        
        String clientId = getId();
        
        String persistedComponent = context.getExternalContext().getRequestParameterMap().get(clientId);
        
        PComponent command = (PComponent) EncodeUtils.decode(persistedComponent);
        
        if (command.getPropertyNames()!=null&&
                command.getPropertyNames().length>0&&
                CommandConstants.ACTION_PROPERTY_NAME.equals(command.getPropertyNames()[0]))
            this.queueEvent(new ActionEvent(this));
    }
    
    private Object[] values;
    
    public Object saveState(FacesContext context) {
        
        if (values == null) {
            values = new Object[4];
        }
        
        values[0] = super.saveState(context);
        values[1] = saveAttachedState(context, actionExpression);
        values[2] = immediate ? Boolean.TRUE : Boolean.FALSE;
        values[3] = immediateSet ? Boolean.TRUE : Boolean.FALSE;
        
        return (values);
        
    }
    
    public void restoreState(FacesContext context, Object state) {
        values = (Object[]) state;
        super.restoreState(context, values[0]);
        actionExpression =
                (MethodExpression) restoreAttachedState(context, values[1]);
        immediate = ((Boolean) values[2]).booleanValue();
        immediateSet = ((Boolean) values[3]).booleanValue();
        
    }
}
